using Microsoft.Xna.Framework;

namespace BlobSim
{
    class ObjectRotation
    {
        private Matrix _rotationMatrix;
        private readonly ObjectRotationSettings _settings;

        private Vector2 _rotationSpeed;

        private Vector3 _lastCameraOrientation;
        private readonly SwipeAccumulator _swipeAccumulator;

        public ObjectRotation()
            : this (new ObjectRotationSettings())
        {
        }

        public ObjectRotation(ObjectRotationSettings settings)
        {
            _rotationMatrix = Matrix.Identity;
            _rotationSpeed = Vector2.Zero;
            _settings = settings;

            _lastCameraOrientation = Vector3.UnitZ;
            _swipeAccumulator = new SwipeAccumulator();
        }

        public Matrix GetRotationMatrix()
        {
            return _rotationMatrix;
        }

        public void PanRotate(Vector2 delta, Vector3 cameraOrientation, GameTime gameTime)
        {
            _lastCameraOrientation = cameraOrientation;
            DoPanRotate(delta);
            _swipeAccumulator.AddTouchMove(delta, gameTime.ElapsedGameTime);
        }

        public void TwistRotate(Vector3 axis, float delta, GameTime gameTime)
        {
            DoTwistRotate(axis, delta);
        }

        public void UpdateRotation(GameTime gameTime)
        {
            if (!_swipeAccumulator.IsEmpty())
            {
                _rotationSpeed = _swipeAccumulator.GetSwipeResidue();
                _swipeAccumulator.ClearTouchMoves();
            }

            if (_rotationSpeed != Vector2.Zero)
            {
                var newSpeedValue = _settings.RotationDampener.GetNewSpeed(_rotationSpeed.Length(), gameTime.ElapsedGameTime);
                _rotationSpeed = Vector2.Normalize(_rotationSpeed) * newSpeedValue;
            }

            DoPanRotate(_rotationSpeed);
        }

        private void DoPanRotate(Vector2 delta)
        {
            var camZ = _lastCameraOrientation;
            camZ.Normalize();
            var camX = Vector3.Cross(camZ, Vector3.Up);
            camX.Normalize();
            var camY = Vector3.Cross(camX, camZ);
            camY.Normalize();

            _rotationMatrix *= Matrix.CreateFromAxisAngle(camX, delta.Y);
            _rotationMatrix *= Matrix.CreateFromAxisAngle(camY, delta.X);
        }

        private void DoTwistRotate(Vector3 axis, float delta)
        {
            _rotationMatrix *= Matrix.CreateFromAxisAngle(axis, delta);
        }
    }
}
